Building the Toolchain
Cross compilation is a complicated topic at the best of times. Luckily, with the LLVM project it is somewhat easier than it was a decade ago.
The alternative to this setup is to spin up a QEMU user-space emulation session and then compile the program in there. You will likely need to recompile clang within the emulator, but you will not have to fiddle with linking parameters to the sysroot since the emulator will already have the proper sysroot installed and it will be as if you were installing clang on your native system. All in all, I recommend the QEMU strat, after having played with this cross compilation toolchain for an obscene amount of time.
Required Sources
This tutorial is based on:
- The SiFive cross compilation quickstart which is outdated
- Mcilloni's blog post
- The LLVM cross compilation documentation
Thanks to those project without which this tutorial really would have been impossible.
In order to construct your cross-compilation toolchain, you will require the following repositories:
complete list of system deps, or better yet a nix flake
- The RISCV toolchain from the gcc project
- The LLVM Project for clang
Compiling the RISCV-GNU toolchain
The toolchain provided by GNU gives us:
- The RISCV sysroot.
- The
gcc
compiler for RISCV (clang has a tough time linking with the RISCV sysroot) - A number of other utilities
For reference (and so that you really understand what that means), the sysroot of a system is the necessary system libraries and tools against which standard utilities link to call OS functions.
If you want to read more about the sysroots and all the fun stuff that you can read at the following sites:
-
Clone the repo:
git clone https://github.com/riscv-collab/riscv-gnu-toolchain.git
-
Change to the directory
-
Configure: When compiling the toolchain, you will need to configure it rather liberally. The configuration command I used was:
./configure --prefix=$HOME/riscv --enable-multilib --disable-gdb
For a little more detail:
--prefix
specifies the install directory where you want your utilities to end up--enable-multilib
enables multilib TODO I'm not sure what this means exactly--disable-gdb
since depending on the version you build, gdb will require gmp which is not necessarily installed on the lab machines.
-
After you have configured to your specifications, you can simply run
make
and wait for a bit. Once again, I suggest doing so inside atmux
session.
Simple script
mkdir $HOME/riscv
https://github.com/riscv-collab/riscv-gnu-toolchain.git riscv-toolchain
cd riscv-toolchain
./configure --prefix=$HOME/riscv --enable-multilib --disable-gdb
make
You will likely run out of space on your lab machine if you try to build gem5, the toolchain and clang on the same machine. Consider teaming up if you really want to do this, or maybe using your own machines (after you switch to linux 🧐).
Compiling clang
This is just a good thing to know, so sit tight and hang on.
You may get some errors in the build process. If this is the case for you, don't worry, just continue reading through the binutils section below.
- Clone the repo:
git clone https://github.com/llvm/llvm-project
- Make a build directory:
mkdir build && cd build
- Configure:
if you want a more in-depth explanation of these flags, go to the llvm discourse or the llvm documentation, and remember you can probably ask the TA about these things too.
cmake -DLLVM_TARGETS_TO_BUILD="RISCV" \
-DLLVM_PARALLEL_LINK_JOBS=2 \
-DLLVM_USE_SPLIT_DWARF=True \
-DLLVM_BUILD_TESTS=False \
-DDEFAULT_SYSROOT=$HOME/riscv \
-DLLVM_DEFAULT_TARGET_TRIPLE=riscv64-unknown-elf \
-DLLVM_INCLUDE_BENCHMARKS=OFF \
-DLLVM_INCLUDE_EXAMPLES=OFF \
-DLLVM_INSTALL_UTILS=ON \
-DLLVM_ENABLE_ASSERTIONS=ON \
-DLLVM_BINUTILS_INCDIR="/usr/include" \ # you might have issues with this
-DBUILD_SHARED_LIBS=ON \
-DLLVM_USE_LINKER=ld \ # if you have lld, you should probably use it
-DCMAKE_C_COMPILER=gcc \ # if you have clang, you should probably use it
-DCMAKE_CXX_COMPILER=g++ \ # ^^
-DCMAKE_BUILD_TYPE=Release \
-G Ninja ../llvm - Run the regular make job, but this time with ninja
ninja -j4
- Make a directory in which you want the installed files to be:
mkdir llvm-18
- Cmake install them:
cmake --install . --prefix $BASE_DIR/llvm-18
, where$BASE_DIR
is some directory of your choice
Simplified Script
#!/usr/bin/env sh
# Create and enter build directory
if [[ "$1" = "-i" ]]
then
BUILD_DIR="\${HOME}/llvm"
mkdir "\${BUILD_DIR}"
cd "\${BUILD_DIR}"
git clone --depth 1 --branch llvmorg-17.0.0 https://github.com/llvm/llvm-project.git llvm-project
else
BUILD_DIR="${HOME}/llvm"
fi
cd llvm-project
mkdir -p build
cd build
# Build llvm, clang, mlir, and lld
# -DLLVM_ENAVBLE_PROJECTS="clang;mlir;lld" \
# lldb;clang-tools-extra
cmake -DLLVM_TARGETS_TO_BUILD="RISCV" \
-DLLVM_PARALLEL_LINK_JOBS=2 \
-DLLVM_USE_SPLIT_DWARF=True \
-DLLVM_BUILD_TESTS=False \
-DDEFAULT_SYSROOT=/home/achilibe/Code/riscv-gnu-toolchain/installed-tools/riscv64-unknown-elf \
-DLLVM_DEFAULT_TARGET_TRIPLE=riscv64-unknown-elf \
-DLLVM_INCLUDE_BENCHMARKS=OFF \
-DLLVM_INCLUDE_EXAMPLES=OFF \
-DLLVM_INSTALL_UTILS=ON \
-DLLVM_ENABLE_ASSERTIONS=ON \
-DLLVM_BINUTILS_INCDIR="/home/achilibe/Code/binutils/include" \
-DBUILD_SHARED_LIBS=ON \
-DLLVM_USE_LINKER=lld-15 \
-DCMAKE_C_COMPILER=clang-15 \
-DCMAKE_CXX_COMPILER=clang++-15 \
-DCMAKE_BUILD_TYPE=Release \
-G Ninja ../llvm
ninja -j4
DIRNAME="llvm-18-rv64"
mkdir "${BUILD_DIR}/$DIRNAME"
cmake --install . --prefix "${BUILD_DIR}/$DIRNAME"
rm -r "\${BUILD_DIR}/llvm-project/build"
Getting some sort of Missing File Error?
If you obtain a missing file error similar to the following:
CMake Error at tools/lto/cmake_install.cmake:52 (file):
file INSTALL cannot find
".../llvm/llvm-project/build/lib/libLTO.so.18.1": No such file or directory.
Call Stack (most recent call first):
tools/cmake_install.cmake:47 (include)
cmake_install.cmake:127 (include)
Then you need to build binutils. This is a pretty common problem, unfortunately it takes some extra time...
Building Binutils
- Clone the binutils repo:
git clone --depth 1 --branch binutils-2_39 https://sourceware.org/git/binutils-gdb.git binutils
cd binutils
- Create a build directory:
mkdir build && cd build
- Configure:
../configure --enable-gold --enable-plugins --disable-werror
make
Script
Once you make the binutils, you can then set the build directory as the -DLLVM_BINUTILS_INCDIR
for the llvm configuration and all should work.
git clone --depth 1 --branch binutils-2_39 \
https://sourceware.org/git/binutils-gdb.git binutils
cd binutils
mkdir build; cd build
../configure --enable-gold --enable-plugins --disable-werror
make all-gold